1. Low Memory Killer机制

[toc]

Andorid 的 Low Memory Killer 是在标准的 Linux Kernel 的 OOM Killer 基础上修改而来的一种内存管理机制。当系统内存不足时,杀死不重要的进程以释放其内存。LMK 的关键参数有 3 个:

  • oom_adj:在 Framework 层使用,代表进程的优先级,数值越高,优先级越低,越容易被杀死。
  • oom_adj threshold:在 Framework 层使用,代表 oom_adj 的内存阈值。Android Kernel 会定时检测当前剩余内存是否低于这个阀值,若低于则杀死 oom_adj ≥ 该阈值对应的 oom_adj 中,数值最大的进程,直到剩余内存恢复至高于该阀值的状态。
  • oom_score_adj: 在 Kernel 层使用,由 oom_adj 换算而来,是杀死进程时实际使用的参数。

1.1. 1. oom_adj

取值范围定义:

-> ProcessList(AOSP, master 分支)

    // These are the various interesting memory levels that we will give to
    // the OOM killer.  Note that the OOM killer only supports 6 slots, so we
    // can't give it a different value for every possible kind of process.
    private final int[] mOomAdj = new int[] {
            FOREGROUND_APP_ADJ, VISIBLE_APP_ADJ, PERCEPTIBLE_APP_ADJ,
            BACKUP_APP_ADJ, CACHED_APP_MIN_ADJ, CACHED_APP_MAX_ADJ
    };
复制代码

以上仅是 LMK 杀死进程时使用的 adj,实际上该类中定义了更多的 adj:

常量定义 常量取值 含义
NATIVE_ADJ -1000 native进程(不被系统管理)
SYSTEM_ADJ -900 系统进程
PERSISTENT_PROC_ADJ -800 系统persistent进程,比如telephony
PERSISTENT_SERVICE_ADJ -700 关联着系统或persistent进程
FOREGROUND_APP_ADJ 0 前台进程
VISIBLE_APP_ADJ 100 可见进程
PERCEPTIBLE_APP_ADJ 200 可感知进程,比如后台音乐播放
BACKUP_APP_ADJ 300 备份进程
HEAVY_WEIGHT_APP_ADJ 400 后台的重量级进程,system/rootdir/init.rc文件中设置
SERVICE_ADJ 500 服务进程
HOME_APP_ADJ 600 Home进程
PREVIOUS_APP_ADJ 700 上一个App的进程
SERVICE_B_ADJ 800 B List中的Service(较老的、使用可能性更小)
CACHED_APP_MIN_ADJ 900 不可见进程的adj最小值
CACHED_APP_MAX_ADJ 906 不可见进程的adj最大值
UNKNOWN_ADJ 1001 一般指将要会缓存进程,无法获取确定值

以上常量在 Android 6.0(API23)及之前版本的取值范围为 [-17, 16]:ProcessList(AOSP,marshmallow-release 分支)

规律:取值越大,重要性越低,进程越容易被杀死。

当触发 LowMemoryKiller 机制时,可根据日志中进程的 adj 值,具体分析进程是在什么状态下被杀死的。

1.2. 2. oom_adj threshold

取值范围定义:

-> ProcessList(AOSP, master 分支)

    // The actual OOM killer memory levels we are using.
    private final int[] mOomMinFree = new int[mOomAdj.length];
复制代码

具体取值由下面两个变量经过换算得到:

    // These are the low-end OOM level limits.  This is appropriate for an
    // HVGA or smaller phone with less than 512MB.  Values are in KB.
    private final int[] mOomMinFreeLow = new int[] {
            12288, 18432, 24576,
            36864, 43008, 49152
    };
    // These are the high-end OOM level limits.  This is appropriate for a
    // 1280x800 or larger screen with around 1GB RAM.  Values are in KB.
    private final int[] mOomMinFreeHigh = new int[] {
            73728, 92160, 110592,
            129024, 147456, 184320
    };
复制代码

数组初始化或更新的方法:

    private void updateOomLevels(int displayWidth, int displayHeight, boolean write) {
        float scaleMem = ((float)(mTotalMemMb-350))/(700-350);
        int minSize = 480*800;  //  384000
        int maxSize = 1280*800; // 1024000  230400 870400  .264
        float scaleDisp = ((float)(displayWidth*displayHeight)-minSize)/(maxSize-minSize);
        float scale = scaleMem > scaleDisp ? scaleMem : scaleDisp;
        ...省略
        final boolean is64bit = Build.SUPPORTED_64_BIT_ABIS.length > 0;
        for (int i=0; i<mOomAdj.length; i++) {
            int low = mOomMinFreeLow[i];
            int high = mOomMinFreeHigh[i];
            if (is64bit) {
                // Increase the high min-free levels for cached processes for 64-bit
                if (i == 4) high = (high*3)/2;
                else if (i == 5) high = (high*7)/4;
            }
            mOomMinFree[i] = (int)(low + ((high-low)*scale));
        }
        ...省略
        if (write) {
            ByteBuffer buf = ByteBuffer.allocate(4 * (2*mOomAdj.length + 1));
            buf.putInt(LMK_TARGET);
            for (int i=0; i<mOomAdj.length; i++) {
                // 除以了 PAGE_SIZE,所以 minfree 中的单位为页,及 4KB
                buf.putInt((mOomMinFree[i]*1024)/PAGE_SIZE);
                buf.putInt(mOomAdj[i]);
            }
            // 向 lmkd 进程发送 LMK_TARGET 命令,
            // 将 oom_adj 阈值写入 "/sys/module/lowmemorykiller/parameters/minfree" 
            // 将 oom_adj 写入 "/sys/module/lowmemorykiller/parameters/adj"
            writeLmkd(buf);
            SystemProperties.set("sys.sysctl.extra_free_kbytes", Integer.toString(reserve));
        }
        // GB: 2048,3072,4096,6144,7168,8192
        // HC: 8192,10240,12288,14336,16384,20480
    }
复制代码

在 ActivityManagerService 调用 updateConfiguration() 的过程中会调用该方法,根据设备的分辨率初始化或更新阈值的大小。

1.3. 3. oom_score_adj

取值范围定义在 Linux Kernel 中:

-> oom.h

#ifndef _UAPI__INCLUDE_LINUX_OOM_H
#define _UAPI__INCLUDE_LINUX_OOM_H

/*
 * /proc/<pid>/oom_score_adj set to OOM_SCORE_ADJ_MIN disables oom killing for
 * pid.
 */
#define OOM_SCORE_ADJ_MIN    (-1000)
#define OOM_SCORE_ADJ_MAX    1000

/*
 * /proc/<pid>/oom_adj set to -17 protects from the oom killer for legacy
 * purposes.
 */
#define OOM_DISABLE (-17)
/* inclusive */
#define OOM_ADJUST_MIN (-16)
#define OOM_ADJUST_MAX 15

#endif /* _UAPI__INCLUDE_LINUX_OOM_H */
复制代码

oom_adj 到 oom_score_adj 的换算方法定义在 Linux Driver 中:

-> lowmemorykiller.c

static int lowmem_oom_adj_to_oom_score_adj(int oom_adj)
{
    if (oom_adj == OOM_ADJUST_MAX)
        return OOM_SCORE_ADJ_MAX;
    else
        return (oom_adj * OOM_SCORE_ADJ_MAX) / -OOM_DISABLE;
}
复制代码

1.4. 4. 工作流程

简述 LMK 的工作流程

1.4.1. 4.1 启动 lmkd

lmkd 是由 init进程通过解析 init.rc 文件来启动的守护进程。lmkd 会创建名为 lmkd 的 socket,节点位于 /dev/socket/lmkd,该 socket 用于跟上层 framework 交互:

img

service lmkd /system/bin/lmkd
    class core
    group root readproc
    critical
    socket lmkd seqpacket 0660 system system
    writepid /dev/cpuset/system-background/tasks
复制代码

lmkd 启动后,便会进入循环等待状态,接受来自 ProcessList 的三个命令:

命令 功能 方法
LMK_TARGET 初始化 oom_adj ProcessList::setOomAdj()
LMK_PROCPRIO 更新 oom_adj ProcessList::updateOomLevels()
LMK_PROCREMOVE 移除进程(暂时无用) ProcessList::remove()

1.4.2. 4.2 初始化 oom_adj

在 ActivityManagerService 调用 updateConfiguration() 的过程中会调用 ProcessList::updateOomLevels() 方法,根据设备的分辨率调整阈值的大小,通过 LMK_TARGET 命令,通知 lmkd(low memory killer deamon)分别向 /sys/module/lowmemorykiller/parameters 目录下的 minfree 和 adj 节点写入相应信息:

img

1.4.3. 4.3 更新 oom_adj

在 ActivityManagerService 调用 applyOomAdjLocked() 的过程中会调用 ProcessList::setOomAdj() 方法,通过 LMK_PROCPRIO 命令,通知 lmkd 向 /proc/进程号/oom_score_adj 写入 oomadj:

D:\Android\projects\wanandroid_java>adb shell ps | findstr wanandroid
u0_a71    6461  1285  1177696 84024 SyS_epoll_ b131e424 S com.yuloran.wanandroid_java

D:\Android\projects\wanandroid_java>adb shell ls /proc/6461/
...省略
oom_adj
oom_score
oom_score_adj
...
复制代码

ActivityManagerService 会根据当前应用进程托管组件(即四大组件)生命周期的变化,及时的调用 applyOomAdjLocked(),更新进程状态及该状态对应的 oom_adj。

进程状态表:

-> ActivityManager.java(AOSP,branch:master)

常量定义 常量取值 含义
PROCESS_STATE_UNKNOWN -1 非真实的进程状态
PROCESS_STATE_PERSISTENT 0 persistent 系统进程
PROCESS_STATE_PERSISTENT_UI 1 persistent 系统进程,并正在执行UI操作
PROCESS_STATE_TOP 2 拥有当前用户可见的 top Activity
PROCESS_STATE_FOREGROUND_SERVICE 3 托管一个前台 Service 的进程
PROCESS_STATE_BOUND_FOREGROUND_SERVICE 4 托管一个由系统绑定的前台 Service 的进程
PROCESS_STATE_IMPORTANT_FOREGROUND 5 对用户很重要的进程,用户可感知其存在
PROCESS_STATE_IMPORTANT_BACKGROUND 6 对用户很重要的进程,用户不可感知其存在
PROCESS_STATE_TRANSIENT_BACKGROUND 7 Process is in the background transient so we will try to keep running.
PROCESS_STATE_BACKUP 8 后台进程,正在运行backup/restore操作
PROCESS_STATE_SERVICE 9 后台进程,且正在运行service
PROCESS_STATE_RECEIVER 10 后台进程,且正在运行receiver
PROCESS_STATE_TOP_SLEEPING 11 与 PROCESS_STATE_TOP 一样,但此时设备正处于休眠状态
PROCESS_STATE_HEAVY_WEIGHT 12 后台进程,但无法执行restore,因此尽量避免kill该进程
PROCESS_STATE_HOME 13 后台进程,且拥有 home Activity
PROCESS_STATE_LAST_ACTIVITY 14 后台进程,且拥有上一次显示的 Activity
PROCESS_STATE_CACHED_ACTIVITY 15 进程处于 cached 状态,且内含 Activity
PROCESS_STATE_CACHED_ACTIVITY_CLIENT 16 进程处于 cached 状态,且为另一个 cached 进程(内含 Activity)的 client 进程
PROCESS_STATE_CACHED_RECENT 17 进程处于 cached 状态,且内含与当前最近任务相对应的 Activity
PROCESS_STATE_CACHED_EMPTY 18 进程处于 cached 状态,且为空进程
PROCESS_STATE_NONEXISTENT 19 不存在的进程

同一个进程,在不同状态下,其 oom_adj 是不一样的。

1.4.4. 4.4 kill 并移除进程

在 ActivityManagerService 调用 updateOomAdjLocked() 时,会判断进程是否需要被杀死,若是,则调用 ProceeRecord::kill() 方法杀死该进程:

img

: 目前 LMK_PROCREMOVE 命令暂时无用,即未执行有意义的代码。

1.5. 参考


Copyright © tracyliu-FE 2021 all right reserved,powered by Gitbook文件修订时间: 2022-03-06 12:52:33

results matching ""

    No results matching ""